#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
真实数据获取器 - 使用多种方式获取A股真实数据
解决网络代理问题，确保能获取到真实的股票数据
"""

import akshare as ak
import pandas as pd
import numpy as np
import requests
import time
import os
from datetime import datetime, timedelta
import logging
import warnings
warnings.filterwarnings('ignore')

logger = logging.getLogger(__name__)

class RealDataFetcher:
    """真实数据获取器 - 优化版本"""
    
    def __init__(self):
        self.session = self._create_session()
        # 多层缓存系统
        self.cache = {}  # 内存缓存
        self.cache_dir = "data_cache"  # 本地缓存目录
        self._ensure_cache_dir()
        
        # 数据源配置
        self.data_sources = {
            'akshare': {'priority': 1, 'status': 'active'},
            'yfinance': {'priority': 2, 'status': 'active'},
            'eastmoney': {'priority': 3, 'status': 'active'}
        }
        
        # 优化的请求控制 - 超快速度
        self.request_interval = 0.3  # 进一步减少到0.3秒间隔
        self.max_retries = 2  # 减少重试次数，提高速度
        self.last_request_time = 0
        self.random_delay_range = (0.1, 0.3)  # 进一步减少随机延迟范围
        
        # 动态速率控制
        self.consecutive_success = 0
        self.min_interval = 0.1  # 最小间隔
        self.max_interval = 1.0  # 最大间隔
        self.speed_mode = 'fast'  # 默认快速模式
        
        # 反爬虫配置
        self.user_agents = [
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        ]
        
        # 数据质量控制
        self.quality_checks = True
        self.data_stats = {
            'total_requests': 0,
            'success_requests': 0,
            'failed_requests': 0,
            'cache_hits': 0
        }
        
        # 连续失败保护
        self.consecutive_failures = 0  # 连续失败次数
        self.max_consecutive_failures = 3  # 降低到3次
        self.failure_pause = False  # 是否因连续失败暂停
        self.failure_pause_start_time = None  # 暂停开始时间
        
        # 离线数据支持
        self.offline_mode = False
        self.offline_data = self._load_offline_data()
        
    def _create_session(self):
        """创建增强的无代理session"""
        session = requests.Session()
        session.proxies = {'http': None, 'https': None}
        
        # 随机选择User-Agent
        import random
        user_agent = random.choice(self.user_agents) if hasattr(self, 'user_agents') else 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        
        session.headers.update({
            'User-Agent': user_agent,
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
            'Accept-Encoding': 'gzip, deflate',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1'
        })
        return session
    
    def _disable_proxy(self):
        """禁用环境变量中的代理设置"""
        proxy_vars = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy']
        for var in proxy_vars:
            if var in os.environ:
                del os.environ[var]
        os.environ['NO_PROXY'] = '*'
    
    def get_stock_list(self):
        """获取A股股票列表 - 增强版，获取完整信息"""
        try:
            self._disable_proxy()
            print("🔄 尝试获取A股股票列表...")
            
            # 首先尝试从缓存获取
            cache_key = "stock_list_complete"
            cached_data = self._get_from_cache(cache_key)
            if cached_data is not None:
                print(f"✅ 从缓存获取股票列表: {len(cached_data)} 只股票")
                return cached_data
            
            # 使用akshare获取股票列表
            stock_list = ak.stock_info_a_code_name()
            
            if stock_list.empty:
                print("❌ 无法获取A股股票列表")
                print("💡 请检查网络连接或akshare数据源可用性")
                return self._get_fallback_stock_list()
            
            print(f"✅ 成功获取A股股票列表: {len(stock_list)} 只股票")
            
            # 获取完整的股票信息
            result_df = stock_list[['code', 'name']].copy()
            
            # 批量获取当前价格和市值数据
            print("🔄 正在获取股票价格和市值数据...")
            self._enrich_stock_list_data(result_df)
            
            # 保存到缓存
            self._save_to_cache(cache_key, result_df, expire_hours=24)
            
            logger.info(f"获取到 {len(result_df)} 只A股完整信息")
            return result_df
            
        except Exception as e:
            print(f"❌ 获取股票列表失败: {e}")
            print("💡 尝试使用备用数据源...")
            logger.warning(f"获取股票列表失败: {e}")
            return self._get_fallback_stock_list()
    
    def _create_sample_stock_list(self):
        """创建样本股票列表"""
        sample_stocks = [
            {'code': '000001', 'name': '平安银行', 'price': 12.85, 'market_cap': 248.7},
            {'code': '000002', 'name': '万科A', 'price': 6.89, 'market_cap': 758.2},
            {'code': '600519', 'name': '贵州茅台', 'price': 16.50, 'market_cap': 2070.3},
            {'code': '600036', 'name': '招商银行', 'price': 18.65, 'market_cap': 965.4},
            {'code': '002475', 'name': '立讯精密', 'price': 15.23, 'market_cap': 107.8},
            {'code': '000858', 'name': '五粮液', 'price': 8.95, 'market_cap': 346.8},
            {'code': '002594', 'name': '比亚迪', 'price': 11.75, 'market_cap': 342.5},
            {'code': '300750', 'name': '宁德时代', 'price': 9.12, 'market_cap': 401.2},
            {'code': '600900', 'name': '长江电力', 'price': 13.45, 'market_cap': 301.8},
            {'code': '000063', 'name': '中兴通讯', 'price': 7.82, 'market_cap': 377.6},
            {'code': '002415', 'name': '海康威视', 'price': 14.23, 'market_cap': 133.9},
            {'code': '300059', 'name': '东方财富', 'price': 10.56, 'market_cap': 168.4},
            {'code': '000776', 'name': '广发证券', 'price': 9.87, 'market_cap': 59.8},
            {'code': '601318', 'name': '中国平安', 'price': 17.93, 'market_cap': 327.5},
            {'code': '600196', 'name': '复星医药', 'price': 11.34, 'market_cap': 99.7},
            {'code': '002230', 'name': '科大讯飞', 'price': 16.78, 'market_cap': 366.1},
            {'code': '300015', 'name': '爱尔眼科', 'price': 8.65, 'market_cap': 138.2},
            {'code': '002142', 'name': '宁波银行', 'price': 19.45, 'market_cap': 187.3},
            {'code': '000725', 'name': '京东方A', 'price': 4.56, 'market_cap': 158.7},
            {'code': '600309', 'name': '万华化学', 'price': 13.87, 'market_cap': 366.9}
        ]
        
        return pd.DataFrame(sample_stocks)
    
    def get_stock_data(self, stock_code: str, days: int = 120):
        """获取股票历史数据 - 优化版本"""
        # 确保股票代码格式正确
        stock_code = str(stock_code).zfill(6)
        
        # 检查是否需要暂停运行
        if self.failure_pause and self.consecutive_failures >= self.max_consecutive_failures:
            raise Exception(f"连续失败 {self.consecutive_failures} 只股票，已达到最大失败次数限制，暂停本次运行")
        
        # 统计请求
        self.data_stats['total_requests'] += 1
        
        # 检查缓存
        cache_key = f"stock_data_{stock_code}_{days}"
        cached_data = self._load_from_cache(cache_key, max_age=3600)  # 1小时有效期
        if cached_data is not None:
            print(f"✅ 从缓存获取 {stock_code} 数据: {len(cached_data)} 条记录")
            self.consecutive_failures = 0  # 重置连续失败计数
            return cached_data
        
        # 按优先级尝试数据源
        for source_name, source_config in sorted(self.data_sources.items(), 
                                                 key=lambda x: x[1]['priority']):
            if source_config['status'] != 'active':
                continue
                
            try:
                print(f"🔄 尝试从 {source_name} 获取 {stock_code} 数据...")
                self._rate_limit()  # 速率限制
                
                # 根据数据源获取数据
                if source_name == 'akshare':
                    data = self._get_from_akshare(stock_code, days)
                elif source_name == 'yfinance':
                    data = self._get_from_yfinance(stock_code, days)
                elif source_name == 'eastmoney':
                    data = self._get_from_eastmoney(stock_code, days)
                else:
                    continue
                
                # 验证数据质量
                if not data.empty and self._validate_stock_data(data, stock_code):
                    # 保存到缓存
                    self._save_to_cache(cache_key, data)
                    self.data_stats['success_requests'] += 1
                    self.consecutive_failures = 0  # 重置连续失败计数
                    self._on_request_success()  # 智能速率控制
                    print(f"✅ 成功获取 {stock_code} 数据: {len(data)} 条记录")
                    return data
                    
            except Exception as e:
                print(f"⚠️ {source_name} 获取失败: {e}")
                self._log_request_failure(stock_code, source_name, e, stage="获取股票数据")
                continue
        
        # 所有数据源都失败
        self.data_stats['failed_requests'] += 1
        self.consecutive_failures += 1
        self._on_request_failure()  # 智能速率控制
        print(f"❌ 所有数据源都无法获取 {stock_code} 数据 (连续失败: {self.consecutive_failures}/{self.max_consecutive_failures})")
        
        # 检查是否需要暂停
        if self.failure_pause and self.consecutive_failures >= self.max_consecutive_failures:
            raise Exception(f"连续失败 {self.consecutive_failures} 只股票，已达到最大失败次数限制，暂停本次运行")
        
        return pd.DataFrame()
    
    def _get_from_akshare(self, stock_code: str, days: int):
        """从akshare获取数据"""
        try:
            self._disable_proxy()
            
            end_date = datetime.now()
            start_date = end_date - timedelta(days=days + 30)
            
            # 重试机制
            for attempt in range(self.max_retries):
                try:
                    hist_data = ak.stock_zh_a_hist(
                        symbol=stock_code,
                        period="daily",
                        start_date=start_date.strftime('%Y%m%d'),
                        end_date=end_date.strftime('%Y%m%d'),
                        adjust="qfq"
                    )
                    
                    if not hist_data.empty:
                        return self._standardize_data(hist_data, 'akshare', days)
                    
                except Exception as e:
                    if attempt < self.max_retries - 1:
                        wait_time = 2 ** attempt
                        print(f"⚠️ akshare第{attempt+1}次尝试失败，等待{wait_time}秒重试...")
                        time.sleep(wait_time)
                    else:
                        raise e
                        
        except Exception as e:
            print(f"❌ akshare获取失败: {e}")
            self._log_request_failure(stock_code, "akshare", e, stage="获取历史数据")
            
        return pd.DataFrame()
    
    def _get_from_yfinance(self, stock_code: str, days: int):
        """从yfinance获取数据"""
        try:
            import yfinance as yf
            
            # 转换股票代码格式
            if stock_code.startswith('6'):
                ticker_symbol = f"{stock_code}.SS"
            else:
                ticker_symbol = f"{stock_code}.SZ"
            
            ticker = yf.Ticker(ticker_symbol)
            hist_data = ticker.history(period=f"{days}d")
            
            if not hist_data.empty:
                return self._standardize_data(hist_data, 'yfinance', days)
                
        except ImportError:
            print("💡 建议安装yfinance: pip install yfinance")
        except Exception as e:
            print(f"❌ yfinance获取失败: {e}")
            self._log_request_failure(stock_code, "yfinance", e, stage="获取历史数据")
            
        return pd.DataFrame()
    
    def _get_from_eastmoney(self, stock_code: str, days: int):
        """从东方财富获取数据（备用方案）"""
        try:
            # 这里可以实现东方财富的数据获取
            # 暂时返回空，作为预留接口
            print("💡 东方财富数据源暂未实现")
            return pd.DataFrame()
            
        except Exception as e:
            print(f"❌ 东方财富获取失败: {e}")
            self._log_request_failure(stock_code, "eastmoney", e, stage="获取历史数据")
            return pd.DataFrame()
    
    def _standardize_data(self, data: pd.DataFrame, source: str, days: int):
        """标准化数据格式"""
        try:
            if source == 'akshare':
                # akshare数据标准化
                expected_columns = ['日期', '开盘', '收盘', '最高', '最低', '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']
                
                if len(data.columns) >= 6:
                    if len(data.columns) == len(expected_columns):
                        data.columns = expected_columns
                    
                    # 重命名为英文列名
                    column_mapping = {
                        '日期': 'date',
                        '开盘': 'open', 
                        '收盘': 'close',
                        '最高': 'high',
                        '最低': 'low',
                        '成交量': 'volume'
                    }
                    
                    for chinese_col, english_col in column_mapping.items():
                        if chinese_col in data.columns:
                            data = data.rename(columns={chinese_col: english_col})
                    
                    # 处理日期索引
                    if 'date' in data.columns:
                        data['date'] = pd.to_datetime(data['date'])
                        data.set_index('date', inplace=True)
                    
            elif source == 'yfinance':
                # yfinance数据标准化
                data.columns = data.columns.str.lower()
                if 'adj close' in data.columns:
                    data = data.rename(columns={'adj close': 'close'})
            
            # 确保数据类型正确
            for col in ['open', 'close', 'high', 'low', 'volume']:
                if col in data.columns:
                    data[col] = pd.to_numeric(data[col], errors='coerce')
            
            # 只保留需要的列
            required_cols = ['open', 'high', 'low', 'close', 'volume']
            available_cols = [col for col in required_cols if col in data.columns]
            data = data[available_cols]
            
            # 排序并截取指定天数
            data = data.sort_index().tail(days)
            
            # 删除包含NaN的行
            data = data.dropna()
            
            return data
            
        except Exception as e:
            print(f"❌ 数据标准化失败: {e}")
            return pd.DataFrame()
    
    def _try_akshare_data(self, stock_code: str, days: int):
        """尝试使用akshare获取真实数据"""
        try:
            # 确保股票代码格式正确
            stock_code = str(stock_code).zfill(6)
            
            self._disable_proxy()
            print(f"🔄 尝试通过akshare获取 {stock_code} 数据...")
            
            end_date = datetime.now()
            start_date = end_date - timedelta(days=days + 30)
            
            # 尝试获取历史数据
            hist_data = ak.stock_zh_a_hist(
                symbol=stock_code,
                period="daily",
                start_date=start_date.strftime('%Y%m%d'),
                end_date=end_date.strftime('%Y%m%d'),
                adjust="qfq"
            )
            
            if not hist_data.empty:
                print(f"✅ akshare原始数据获取成功: {len(hist_data)} 条，列数: {len(hist_data.columns)}")
                print(f"📊 原始列名: {list(hist_data.columns)}")
                
                # 根据实际返回的列进行处理
                expected_columns = ['日期', '开盘', '收盘', '最高', '最低', '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']
                
                if len(hist_data.columns) >= 6:  # 至少有基本的OHLCV数据
                    # 重命名列
                    if len(hist_data.columns) == len(expected_columns):
                        hist_data.columns = expected_columns
                    
                    # 标准化列名为英文
                    column_mapping = {
                        '日期': 'date',
                        '开盘': 'open', 
                        '收盘': 'close',
                        '最高': 'high',
                        '最低': 'low',
                        '成交量': 'volume'
                    }
                    
                    # 只保留存在的列
                    for chinese_col, english_col in column_mapping.items():
                        if chinese_col in hist_data.columns:
                            hist_data = hist_data.rename(columns={chinese_col: english_col})
                    
                    # 处理日期列
                    if 'date' in hist_data.columns:
                        hist_data['date'] = pd.to_datetime(hist_data['date'])
                        hist_data.set_index('date', inplace=True)
                    else:
                        # 如果没有日期列，使用索引作为日期
                        hist_data.index = pd.date_range(end=end_date.date(), periods=len(hist_data), freq='D')
                    
                    # 确保数据类型正确
                    for col in ['open', 'close', 'high', 'low', 'volume']:
                        if col in hist_data.columns:
                            hist_data[col] = pd.to_numeric(hist_data[col], errors='coerce')
                    
                    # 只保留需要的列
                    required_cols = ['open', 'high', 'low', 'close', 'volume']
                    available_cols = [col for col in required_cols if col in hist_data.columns]
                    hist_data = hist_data[available_cols]
                    
                    hist_data = hist_data.sort_index().tail(days)
                    print(f"✅ akshare数据处理完成: {len(hist_data)} 条")
                    return hist_data
                else:
                    print(f"⚠️ akshare返回的列数不足: {len(hist_data.columns)}")
                    
        except Exception as e:
            print(f"❌ akshare获取 {stock_code} 失败: {e}")
            self._log_request_failure(stock_code, "akshare", e, stage="尝试获取真实数据")
            
        return pd.DataFrame()
    
    def _try_alternative_sources(self, stock_code: str, days: int):
        """尝试其他数据源获取真实数据"""
        try:
            # 尝试使用yfinance作为备用数据源
            import yfinance as yf
            
            # 将A股代码转换为Yahoo Finance格式
            if stock_code.startswith('6'):
                ticker_symbol = f"{stock_code}.SS"  # 上海交易所
            else:
                ticker_symbol = f"{stock_code}.SZ"  # 深圳交易所
                
            print(f"🔄 尝试通过yfinance获取 {stock_code} 数据...")
            
            ticker = yf.Ticker(ticker_symbol)
            hist_data = ticker.history(period=f"{days}d")
            
            if not hist_data.empty:
                # 标准化列名为小写
                hist_data.columns = hist_data.columns.str.lower()
                hist_data = hist_data.rename(columns={'adj close': 'close'})
                
                # 只保留需要的列
                hist_data = hist_data[['open', 'high', 'low', 'close', 'volume']]
                
                print(f"✅ yfinance获取成功: {len(hist_data)} 条记录")
                return hist_data
                
        except ImportError:
            print("💡 建议安装yfinance作为备用数据源: pip install yfinance")
        except Exception as e:
            print(f"⚠️ yfinance获取失败: {e}")
            self._log_request_failure(stock_code, "yfinance", e, stage="尝试获取真实数据")
            
        # 尝试使用tushare（如果有）
        try:
            import tushare as ts
            print(f"🔄 尝试通过tushare获取 {stock_code} 数据...")
            
            # 需要设置token，这里先跳过
            print("💡 tushare需要token配置，暂时跳过")
            
        except ImportError:
            print("💡 可以安装tushare作为备用数据源: pip install tushare")
        except Exception as e:
            print(f"⚠️ tushare获取失败: {e}")
            self._log_request_failure(stock_code, "tushare", e, stage="尝试获取真实数据")
            
        return pd.DataFrame()

    def _generate_realistic_data(self, stock_code: str, days: int):
        """已禁用模拟数据生成 - 强制使用真实数据"""
        print(f"❌ 模拟数据生成已禁用")
        print(f"💡 系统现在只使用真实网络数据")
        print(f"🔧 如果无法获取真实数据，请检查:")
        print(f"   • 网络连接是否正常")
        print(f"   • 股票代码是否正确")
        print(f"   • 数据源是否可用")
        return pd.DataFrame()  # 返回空DataFrame
        
        # 获取基础价格
        base_price = self._get_base_price(stock_code)
        
        # 生成日期序列
        end_date = datetime.now().date()
        dates = pd.date_range(end=end_date, periods=days, freq='D')
        dates = dates[dates.dayofweek < 5]  # 只保留工作日
        
        # 生成价格数据
        data = []
        current_price = base_price
        
        for i, date in enumerate(dates):
            # 模拟价格波动（遵循股市规律）
            daily_return = np.random.normal(0.001, 0.025)  # 日收益率均值0.1%，标准差2.5%
            
            # 添加趋势和周期性
            trend = 0.0002 * np.sin(i / 10)  # 10天周期
            seasonal = 0.0001 * np.sin(i / 60)  # 60天季节性
            
            total_return = daily_return + trend + seasonal
            current_price *= (1 + total_return)
            
            # 确保价格在合理区间
            current_price = max(current_price * 0.7, min(current_price * 1.3, current_price))
            
            # 生成当日的开高低收
            open_price = current_price * np.random.uniform(0.98, 1.02)
            high_price = max(open_price, current_price) * np.random.uniform(1.0, 1.03)
            low_price = min(open_price, current_price) * np.random.uniform(0.97, 1.0)
            close_price = current_price
            
            # 生成成交量（对数正态分布）
            base_volume = 1000000 * np.random.lognormal(0, 0.5)
            
            data.append({
                'open': round(open_price, 2),
                'high': round(high_price, 2), 
                'low': round(low_price, 2),
                'close': round(close_price, 2),
                'volume': int(base_volume)
            })
        
        df = pd.DataFrame(data, index=dates[:len(data)])
        return df
    
    def _get_base_price(self, stock_code: str):
        """获取股票基础价格"""
        price_map = {
            '000001': 12.85, '000002': 6.89, '600519': 16.50, '600036': 18.65,
            '002475': 15.23, '000858': 8.95, '002594': 11.75, '300750': 9.12,
            '600900': 13.45, '000063': 7.82, '002415': 14.23, '300059': 10.56,
            '000776': 9.87, '601318': 17.93, '600196': 11.34, '002230': 16.78,
            '300015': 8.65, '002142': 19.45, '000725': 4.56, '600309': 13.87
        }
        
        return price_map.get(stock_code, np.random.uniform(5, 18))
    
    def get_stock_basic_info(self, stock_code: str):
        """获取股票基本信息 - 多数据源优化版本"""
        # 确保股票代码格式正确
        stock_code = str(stock_code).zfill(6)
        
        # 检查是否需要暂停运行
        if self.failure_pause and self.consecutive_failures >= self.max_consecutive_failures:
            raise Exception(f"连续失败 {self.consecutive_failures} 只股票，已达到最大失败次数限制，暂停本次运行")
        
        # 统计请求
        self.data_stats['total_requests'] += 1
        
        # 检查缓存
        cache_key = f"basic_info_{stock_code}"
        cached_data = self._load_from_cache(cache_key, max_age=1800)  # 30分钟有效期
        if cached_data is not None:
            self.consecutive_failures = 0  # 重置连续失败计数
            return cached_data
        
        # 获取免费数据源配置
        free_sources = self._get_free_data_sources()
        
        # 按优先级尝试每个免费数据源
        for source_name, source_config in sorted(free_sources.items(), 
                                                 key=lambda x: x[1]['priority']):
            if source_config['status'] != 'active':
                continue
                
            try:
                print(f"🔄 尝试从 {source_config['name']} 获取 {stock_code} 基本信息...")
                self._rate_limit()  # 速率限制
                
                result = source_config['method'](stock_code)
                if result and self._validate_basic_info(result):
                    # 保存到缓存
                    self._save_to_cache(cache_key, result)
                    self.data_stats['success_requests'] += 1
                    self.consecutive_failures = 0  # 重置连续失败计数
                    print(f"✅ 成功从 {source_config['name']} 获取 {stock_code} 基本信息")
                    return result
                else:
                    print(f"⚠️ {source_config['name']} 返回数据质量不佳")
                    
            except Exception as e:
                print(f"❌ {source_config['name']} 获取失败: {e}")
                self._log_request_failure(stock_code, source_name, e, stage="获取基本信息")
                continue
        
        # 所有免费数据源都失败，尝试使用离线数据
        self.data_stats['failed_requests'] += 1
        self.consecutive_failures += 1
        print(f"❌ 所有免费数据源都失败 (连续失败: {self.consecutive_failures}/{self.max_consecutive_failures})")
        
        # 尝试使用离线数据
        print(f"🔄 尝试使用离线数据获取 {stock_code} 基本信息...")
        offline_info = self._get_offline_basic_info(stock_code)
        if offline_info:
            print(f"✅ 使用离线数据获取 {stock_code} 基本信息")
            self.data_stats['success_requests'] += 1
            self.consecutive_failures = 0  # 重置连续失败计数
            return offline_info
        
        # 检查是否需要暂停
        if self.failure_pause and self.consecutive_failures >= self.max_consecutive_failures:
            raise Exception(f"连续失败 {self.consecutive_failures} 只股票，已达到最大失败次数限制，暂停本次运行")
        
        return {}
    
    def _get_free_data_sources(self):
        """获取免费数据源配置"""
        return {
            'yfinance': {
                'name': 'Yahoo Finance (免费)',
                'priority': 1,
                'method': self._get_basic_info_from_yfinance,
                'status': 'active'
            },
            'akshare_individual': {
                'name': 'akshare单股票查询 (免费)',
                'priority': 2,
                'method': self._get_basic_info_from_akshare_individual,
                'status': 'active'
            },
            'eastmoney_web': {
                'name': '东方财富网页解析 (免费)',
                'priority': 3,
                'method': self._get_basic_info_from_eastmoney_web,
                'status': 'active'
            },
            'sina_web': {
                'name': '新浪财经网页解析 (免费)',
                'priority': 4,
                'method': self._get_basic_info_from_sina_web,
                'status': 'active'
            }
        }
    
    def _get_basic_info_from_yfinance(self, stock_code):
        """从Yahoo Finance获取基本信息"""
        try:
            import yfinance as yf
            
            # 转换股票代码格式
            if stock_code.startswith('6'):
                ticker_symbol = f"{stock_code}.SS"  # 上海交易所
            else:
                ticker_symbol = f"{stock_code}.SZ"  # 深圳交易所
            
            ticker = yf.Ticker(ticker_symbol)
            
            # 获取股票信息
            info = ticker.info
            hist = ticker.history(period="5d")  # 获取最近5天数据
            
            if not hist.empty and info:
                latest_close = hist['Close'].iloc[-1]
                market_cap = info.get('marketCap', 0) / 100000000  # 转换为亿元
                
                return {
                    'code': stock_code,
                    'name': info.get('longName', f'股票{stock_code}'),
                    'current_price': float(latest_close),
                    'close': float(latest_close),
                    'market_cap': market_cap,
                    'pe_ratio': float(info.get('trailingPE', 0)) if info.get('trailingPE') else 15.0,
                    'pb_ratio': float(info.get('priceToBook', 0)) if info.get('priceToBook') else 2.0,
                    'turnover_rate': 3.0,  # yfinance没有换手率，使用默认值
                    'volume': float(hist['Volume'].iloc[-1])
                }
            
        except ImportError:
            print("💡 建议安装yfinance: pip install yfinance")
        except Exception as e:
            self._log_request_failure(stock_code, "yfinance", e, stage="获取基本信息")
        
        return None
    
    def _get_basic_info_from_akshare_individual(self, stock_code):
        """从akshare单股票接口获取基本信息"""
        try:
            import akshare as ak
            self._disable_proxy()
            
            # 使用单股票查询，避免获取全市场数据
            # 尝试获取股票基本信息
            stock_info = ak.stock_individual_info_em(symbol=stock_code)
            if not stock_info.empty:
                # 解析股票信息
                info_dict = {}
                for _, row in stock_info.iterrows():
                    info_dict[row['item']] = row['value']
                
                # 获取最新价格
                spot_data = ak.stock_zh_a_hist(symbol=stock_code, period="daily", 
                                             start_date="20241201", end_date="20241231")
                latest_price = 10.0  # 默认价格
                if not spot_data.empty:
                    latest_price = float(spot_data.iloc[-1]['收盘'])
                
                return {
                    'code': stock_code,
                    'name': info_dict.get('股票简称', f'股票{stock_code}'),
                    'current_price': latest_price,
                    'close': latest_price,
                    'market_cap': float(info_dict.get('总市值', '50000000000')) / 100000000,
                    'pe_ratio': float(info_dict.get('市盈率-动态', '15')),
                    'pb_ratio': float(info_dict.get('市净率', '2')),
                    'turnover_rate': 3.0,
                    'volume': 1000000
                }
            
        except Exception as e:
            self._log_request_failure(stock_code, "akshare_individual", e, stage="获取基本信息")
        
        return None
    
    def _get_basic_info_from_eastmoney_web(self, stock_code):
        """从东方财富网页获取基本信息"""
        try:
            import requests
            import re
            
            # 确定市场代码
            market_code = "1" if stock_code.startswith('6') else "0"
            
            # 东方财富股票页面URL
            url = f"http://quote.eastmoney.com/{market_code}{stock_code}.html"
            
            headers = {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'Cache-Control': 'no-cache'
            }
            
            response = requests.get(url, headers=headers, timeout=10)
            if response.status_code == 200:
                content = response.text
                
                # 提取股票名称
                name_match = re.search(r'<title>([^(]+)', content)
                stock_name = name_match.group(1).strip() if name_match else f'股票{stock_code}'
                
                # 提取当前价格（简化版本）
                price_match = re.search(r'"f43":(\d+)', content)
                current_price = float(price_match.group(1)) / 100 if price_match else 10.0
                
                return {
                    'code': stock_code,
                    'name': stock_name,
                    'current_price': current_price,
                    'close': current_price,
                    'market_cap': 50.0,  # 默认市值
                    'pe_ratio': 15.0,    # 默认PE
                    'pb_ratio': 2.0,     # 默认PB
                    'turnover_rate': 3.0,
                    'volume': 1000000
                }
            
        except Exception as e:
            self._log_request_failure(stock_code, "eastmoney_web", e, stage="获取基本信息")
        
        return None
    
    def _get_basic_info_from_sina_web(self, stock_code):
        """从新浪财经网页获取基本信息"""
        try:
            import requests
            
            # 新浪财经API接口
            market_prefix = "sh" if stock_code.startswith('6') else "sz"
            url = f"http://hq.sinajs.cn/list={market_prefix}{stock_code}"
            
            headers = {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
                'Referer': 'http://finance.sina.com.cn'
            }
            
            response = requests.get(url, headers=headers, timeout=10)
            if response.status_code == 200:
                content = response.text
                
                # 解析新浪返回的数据格式
                if 'var hq_str_' in content:
                    data_part = content.split('"')[1]
                    data_fields = data_part.split(',')
                    
                    if len(data_fields) >= 4:
                        return {
                            'code': stock_code,
                            'name': data_fields[0],
                            'current_price': float(data_fields[3]) if data_fields[3] else 10.0,
                            'close': float(data_fields[3]) if data_fields[3] else 10.0,
                            'market_cap': 50.0,  # 新浪接口没有市值，使用默认值
                            'pe_ratio': 15.0,    # 默认PE
                            'pb_ratio': 2.0,     # 默认PB
                            'turnover_rate': 3.0,
                            'volume': float(data_fields[8]) if len(data_fields) > 8 and data_fields[8] else 1000000
                        }
            
        except Exception as e:
            self._log_request_failure(stock_code, "sina_web", e, stage="获取基本信息")
        
        return None
    
    def _validate_basic_info(self, info: dict) -> bool:
        """验证基本信息质量"""
        try:
            # 检查价格合理性
            if info.get('current_price', 0) <= 0 or info.get('current_price', 0) > 1000:
                return False
            
            # 检查市值合理性
            if info.get('market_cap', 0) <= 0:
                return False
            
            # 检查PE合理性
            pe_ratio = info.get('pe_ratio', 0)
            if pe_ratio < 0 or pe_ratio > 1000:
                return False
            
            return True
            
        except Exception:
            return False
    
    def _get_basic_info_fallback(self, stock_code: str):
        """获取基本信息的备用方案"""
        try:
            # 尝试通过历史数据获取基本信息
            hist_data = ak.stock_zh_a_hist(
                symbol=stock_code, 
                period="daily", 
                start_date="20240101", 
                end_date="20241231"
            )
            
            if not hist_data.empty:
                latest = hist_data.iloc[-1]
                return {
                    'code': stock_code,
                    'name': f'股票{stock_code}',
                    'current_price': float(latest.get('收盘', 0)),
                    'close': float(latest.get('收盘', 0)),
                    'market_cap': 50,  # 默认市值
                    'pe_ratio': 15,    # 默认PE
                    'pb_ratio': 2,     # 默认PB
                    'turnover_rate': 3, # 默认换手率
                    'volume': float(latest.get('成交量', 1000000))
                }
                
        except Exception as e:
            print(f"⚠️ 备用方案获取失败: {e}")
            self._log_request_failure(stock_code, "备用方案", e, stage="获取基本信息")
            
        return None
    
    def _should_refresh_market_data(self):
        """判断是否需要刷新市场数据"""
        if not hasattr(self, '_market_data_time'):
            return True
        # 10分钟刷新一次
        return time.time() - self._market_data_time > 600
    
    def get_dividend_info(self, stock_code: str):
        """获取分红信息 - 只使用真实数据"""
        try:
            self._disable_proxy()
            print(f"🔄 尝试获取 {stock_code} 分红信息...")
            
            # 尝试获取分红送股数据
            try:
                dividend_data = ak.stock_zh_a_dividend(symbol=stock_code)
                if not dividend_data.empty:
                    print(f"✅ 成功获取 {stock_code} 分红信息: {len(dividend_data)} 条记录")
                    return dividend_data
            except Exception as e:
                print(f"⚠️ akshare分红数据获取失败: {e}")
            
            print(f"❌ 无法获取 {stock_code} 真实分红信息")
            print(f"💡 请检查网络连接或股票代码是否正确")
            return pd.DataFrame()
            
        except Exception as e:
            print(f"❌ 获取 {stock_code} 分红信息异常: {e}")
            logger.warning(f"获取 {stock_code} 分红信息失败: {e}")
            return pd.DataFrame()
    
    def _ensure_cache_dir(self):
        """确保缓存目录存在"""
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir)
            
    def _get_cache_path(self, cache_key: str):
        """获取缓存文件路径"""
        return os.path.join(self.cache_dir, f"{cache_key}.pkl")
    
    def _get_from_cache(self, cache_key: str, max_age: int = 3600):
        """从缓存获取数据（别名方法）"""
        return self._load_from_cache(cache_key, max_age)
    
    def _save_to_cache(self, cache_key: str, data, expire_hours: int = 24):
        """保存数据到缓存 - 兼容版本"""
        max_age = expire_hours * 3600
        self._save_to_cache_with_age(cache_key, data, max_age)
    
    def _save_to_cache_with_age(self, cache_key: str, data, max_age: int = 86400):
        """保存数据到缓存（带过期时间）"""
        try:
            # 保存到内存缓存
            self.cache[cache_key] = {
                'data': data,
                'timestamp': time.time(),
                'max_age': max_age
            }
            
            # 保存到本地缓存
            cache_path = self._get_cache_path(cache_key)
            import pickle
            with open(cache_path, 'wb') as f:
                pickle.dump({
                    'data': data,
                    'timestamp': time.time(),
                    'max_age': max_age
                }, f)
        except Exception as e:
            logger.warning(f"保存缓存失败: {e}")

    def _load_from_cache(self, cache_key: str, max_age: int = 3600):
        """从缓存加载数据"""
        try:
            # 先检查内存缓存
            if cache_key in self.cache:
                cached_item = self.cache[cache_key]
                if time.time() - cached_item['timestamp'] < max_age:
                    self.data_stats['cache_hits'] += 1
                    return cached_item['data']
            
            # 检查本地缓存
            cache_path = self._get_cache_path(cache_key)
            if os.path.exists(cache_path):
                import pickle
                with open(cache_path, 'rb') as f:
                    cached_item = pickle.load(f)
                    if time.time() - cached_item['timestamp'] < max_age:
                        # 重新加载到内存缓存
                        self.cache[cache_key] = cached_item
                        self.data_stats['cache_hits'] += 1
                        return cached_item['data']
        except Exception as e:
            logger.warning(f"加载缓存失败: {e}")
        
        return None
    
    def _get_from_cache(self, cache_key: str, max_age: int = 3600):
        """从缓存获取数据（别名方法）"""
        return self._load_from_cache(cache_key, max_age)
    
    def _rate_limit(self):
        """智能请求速率限制 - 优化版本"""
        self._smart_rate_limit()
    
    def _smart_rate_limit(self):
        """智能速率限制 - 根据成功率动态调整"""
        import random
        import time
        
        # 计算距离上次请求的时间
        now = time.time()
        elapsed = now - self.last_request_time
        
        # 动态调整间隔
        current_interval = self._get_dynamic_interval()
        
        if elapsed < current_interval:
            # 基础延迟
            base_delay = current_interval - elapsed
            # 减少随机延迟
            random_delay = random.uniform(0.01, 0.1)  # 减少随机延迟
            total_delay = base_delay + random_delay
            
            if total_delay > 0.2:  # 只有超过0.2秒才显示等待信息
                print(f"⏱️ 速率限制：等待 {total_delay:.2f} 秒...")
            time.sleep(total_delay)
        
        self.last_request_time = time.time()
        
        # 更少频率更换User-Agent
        if random.random() < 0.1:  # 降低到10%概率
            self.session.headers['User-Agent'] = random.choice(self.user_agents)
    
    def _get_dynamic_interval(self):
        """根据成功情况动态调整间隔"""
        # 根据连续成功次数调整间隔
        if self.consecutive_success >= 10:
            # 连续成功多次，可以加快速度
            return self.min_interval
        elif self.consecutive_success >= 5:
            # 适中成功，使用较短间隔
            return (self.min_interval + self.request_interval) / 2
        else:
            # 刚开始或有失败，使用标准间隔
            return self.request_interval
    
    def _on_request_success(self):
        """请求成功时调用"""
        self.consecutive_success += 1
        
    def _on_request_failure(self):
        """请求失败时调用"""
        self.consecutive_success = 0
        # 失败时适当增加间隔
        self.request_interval = min(self.request_interval * 1.2, self.max_interval)
    
    def _load_offline_data(self):
        """加载离线数据"""
        offline_data = {
            'stock_list': [
                {'code': '000001', 'name': '平安银行', 'market_cap': 248.7, 'pe_ratio': 5.8},
                {'code': '000002', 'name': '万科A', 'market_cap': 758.2, 'pe_ratio': 7.2},
                {'code': '600519', 'name': '贵州茅台', 'market_cap': 2070.3, 'pe_ratio': 28.5},
                {'code': '600036', 'name': '招商银行', 'market_cap': 965.4, 'pe_ratio': 6.8},
                {'code': '002594', 'name': '比亚迪', 'market_cap': 342.5, 'pe_ratio': 15.2},
                {'code': '300750', 'name': '宁德时代', 'market_cap': 401.2, 'pe_ratio': 18.9},
                {'code': '600900', 'name': '长江电力', 'market_cap': 301.8, 'pe_ratio': 12.3},
                {'code': '000858', 'name': '五粮液', 'market_cap': 346.8, 'pe_ratio': 22.1},
                {'code': '002415', 'name': '海康威视', 'market_cap': 133.9, 'pe_ratio': 13.5},
                {'code': '300059', 'name': '东方财富', 'market_cap': 168.4, 'pe_ratio': 25.8},
                {'code': '601318', 'name': '中国平安', 'market_cap': 327.5, 'pe_ratio': 8.9},
                {'code': '000063', 'name': '中兴通讯', 'market_cap': 377.6, 'pe_ratio': 19.4},
                {'code': '600309', 'name': '万华化学', 'market_cap': 366.9, 'pe_ratio': 11.7},
                {'code': '002230', 'name': '科大讯飞', 'market_cap': 366.1, 'pe_ratio': 45.8},
                {'code': '000725', 'name': '京东方A', 'market_cap': 158.7, 'pe_ratio': 22.3}
            ],
            'basic_info': {
                '000001': {'name': '平安银行', 'current_price': 12.85, 'market_cap': 248.7, 'pe_ratio': 5.8, 'pb_ratio': 0.95},
                '000002': {'name': '万科A', 'current_price': 6.89, 'market_cap': 758.2, 'pe_ratio': 7.2, 'pb_ratio': 1.1},
                '600519': {'name': '贵州茅台', 'current_price': 1650.0, 'market_cap': 2070.3, 'pe_ratio': 28.5, 'pb_ratio': 8.5},
                '600036': {'name': '招商银行', 'current_price': 45.68, 'market_cap': 965.4, 'pe_ratio': 6.8, 'pb_ratio': 1.2},
                '002594': {'name': '比亚迪', 'current_price': 323.91, 'market_cap': 342.5, 'pe_ratio': 15.2, 'pb_ratio': 4.2},
                '300750': {'name': '宁德时代', 'current_price': 191.8, 'market_cap': 401.2, 'pe_ratio': 18.9, 'pb_ratio': 5.1},
                '600900': {'name': '长江电力', 'current_price': 24.5, 'market_cap': 301.8, 'pe_ratio': 12.3, 'pb_ratio': 1.8},
                '000858': {'name': '五粮液', 'current_price': 128.7, 'market_cap': 346.8, 'pe_ratio': 22.1, 'pb_ratio': 4.9},
                '002415': {'name': '海康威视', 'current_price': 28.9, 'market_cap': 133.9, 'pe_ratio': 13.5, 'pb_ratio': 2.8},
                '300059': {'name': '东方财富', 'current_price': 12.6, 'market_cap': 168.4, 'pe_ratio': 25.8, 'pb_ratio': 3.2},
                '601318': {'name': '中国平安', 'current_price': 40.2, 'market_cap': 327.5, 'pe_ratio': 8.9, 'pb_ratio': 1.1},
                '000063': {'name': '中兴通讯', 'current_price': 15.8, 'market_cap': 377.6, 'pe_ratio': 19.4, 'pb_ratio': 2.1},
                '600309': {'name': '万华化学', 'current_price': 78.5, 'market_cap': 366.9, 'pe_ratio': 11.7, 'pb_ratio': 1.9},
                '002230': {'name': '科大讯飞', 'current_price': 35.7, 'market_cap': 366.1, 'pe_ratio': 45.8, 'pb_ratio': 4.8},
                '000725': {'name': '京东方A', 'current_price': 4.1, 'market_cap': 158.7, 'pe_ratio': 22.3, 'pb_ratio': 1.3}
            }
        }
        return offline_data
    
    def _get_offline_basic_info(self, stock_code):
        """获取离线基本信息"""
        if stock_code in self.offline_data['basic_info']:
            info = self.offline_data['basic_info'][stock_code].copy()
            info['code'] = stock_code
            info['close'] = info['current_price']
            info['turnover_rate'] = 2.5  # 默认换手率
            info['volume'] = 1000000  # 默认成交量
            return info
        return None

    def _validate_stock_data(self, data, stock_code):
        """验证股票数据质量"""
        try:
            if data.empty:
                return False
            
            # 检查必要的列
            required_columns = ['open', 'high', 'low', 'close', 'volume']
            if not all(col in data.columns for col in required_columns):
                return False
            
            # 检查数据完整性
            if data.isnull().sum().sum() > len(data) * 0.1:  # 超过10%的缺失值
                return False
            
            # 检查价格合理性
            latest_data = data.iloc[-1]
            if latest_data['close'] <= 0 or latest_data['close'] > 10000:
                return False
            
            # 检查价格逻辑
            if latest_data['high'] < latest_data['low']:
                return False
            
            return True
            
        except Exception as e:
            logger.warning(f"数据验证失败: {e}")
            return False
    
    def get_data_stats(self):
        """获取数据获取统计信息"""
        if self.data_stats['total_requests'] == 0:
            return {
                'success_rate': 0,
                'cache_hit_rate': 0,
                'total_requests': 0,
                'successful_requests': 0,
                'failed_requests': 0,
                'cache_hits': 0,
                'consecutive_failures': self.consecutive_failures,
                'max_consecutive_failures': self.max_consecutive_failures,
                'failure_pause_enabled': self.failure_pause
            }
        
        return {
            'success_rate': (self.data_stats['success_requests'] / self.data_stats['total_requests']) * 100,
            'cache_hit_rate': (self.data_stats['cache_hits'] / self.data_stats['total_requests']) * 100,
            'total_requests': self.data_stats['total_requests'],
            'successful_requests': self.data_stats['success_requests'],
            'failed_requests': self.data_stats['failed_requests'],
            'cache_hits': self.data_stats['cache_hits'],
            'consecutive_failures': self.consecutive_failures,
            'max_consecutive_failures': self.max_consecutive_failures,
            'failure_pause_enabled': self.failure_pause
        }
    
    def clear_cache(self):
        """清理缓存"""
        try:
            # 清理内存缓存
            self.cache.clear()
            
            # 清理本地缓存
            if os.path.exists(self.cache_dir):
                import shutil
                shutil.rmtree(self.cache_dir)
                self._ensure_cache_dir()
            
            print("✅ 缓存清理完成")
            
        except Exception as e:
            print(f"❌ 缓存清理失败: {e}")
    
    def reset_failure_count(self):
        """重置连续失败计数"""
        self.consecutive_failures = 0
        print("✅ 连续失败计数已重置")
    
    def set_failure_pause(self, enabled: bool):
        """设置是否启用失败暂停"""
        self.failure_pause = enabled
        print(f"✅ 失败暂停功能已{'启用' if enabled else '禁用'}")
    
    def set_max_consecutive_failures(self, max_failures: int):
        """设置最大连续失败次数"""
        self.max_consecutive_failures = max_failures
        print(f"✅ 最大连续失败次数已设置为: {max_failures}")
    
    def get_failure_status(self):
        """获取失败状态"""
        return {
            'consecutive_failures': self.consecutive_failures,
            'max_consecutive_failures': self.max_consecutive_failures,
            'failure_pause_enabled': self.failure_pause,
            'is_near_limit': self.consecutive_failures >= self.max_consecutive_failures - 1
        }
    
    def get_data_stats(self):
        """获取数据获取统计信息"""
        if self.data_stats['total_requests'] == 0:
            return {
                'success_rate': 0,
                'cache_hit_rate': 0,
                'total_requests': 0,
                'successful_requests': 0,
                'failed_requests': 0,
                'cache_hits': 0,
                'consecutive_failures': self.consecutive_failures,
                'max_consecutive_failures': self.max_consecutive_failures,
                'failure_pause_enabled': self.failure_pause
            }
        
        return {
            'success_rate': (self.data_stats['success_requests'] / self.data_stats['total_requests']) * 100,
            'cache_hit_rate': (self.data_stats['cache_hits'] / self.data_stats['total_requests']) * 100,
            'total_requests': self.data_stats['total_requests'],
            'successful_requests': self.data_stats['success_requests'],
            'failed_requests': self.data_stats['failed_requests'],
            'cache_hits': self.data_stats['cache_hits'],
            'consecutive_failures': self.consecutive_failures,
            'max_consecutive_failures': self.max_consecutive_failures,
            'failure_pause_enabled': self.failure_pause
        }
    
    def clear_cache(self):
        """清理缓存"""
        try:
            # 清理内存缓存
            self.cache.clear()
            
            # 清理本地缓存
            if os.path.exists(self.cache_dir):
                import shutil
                shutil.rmtree(self.cache_dir)
                self._ensure_cache_dir()
            
            print("✅ 缓存清理完成")
            
        except Exception as e:
            print(f"❌ 缓存清理失败: {e}")
    
    def _get_alternative_data_sources(self):
        """获取备用数据源列表"""
        return {
            'akshare_em': {
                'name': 'akshare东方财富',
                'priority': 1,
                'method': self._get_from_akshare_em,
                'status': 'active'
            },
            'akshare_sina': {
                'name': 'akshare新浪',
                'priority': 2, 
                'method': self._get_from_akshare_sina,
                'status': 'active'
            },
            'akshare_qq': {
                'name': 'akshare腾讯',
                'priority': 3,
                'method': self._get_from_akshare_qq,
                'status': 'active'
            },
            'direct_request': {
                'name': '直接请求',
                'priority': 4,
                'method': self._get_from_direct_request,
                'status': 'active'
            }
        }
    
    def _get_from_akshare_em(self, stock_code):
        """从akshare东方财富获取数据"""
        try:
            import akshare as ak
            self._disable_proxy()
            
            # 获取实时行情
            df = ak.stock_zh_a_spot_em()
            if not df.empty:
                stock_info = df[df['代码'] == stock_code]
                if not stock_info.empty:
                    return self._parse_em_data(stock_info.iloc[0])
            return None
        except Exception as e:
            print(f"⚠️ 东方财富数据源失败: {e}")
            self._log_request_failure(stock_code, "akshare_em", e, stage="获取数据")
            return None
    
    def _get_from_akshare_sina(self, stock_code):
        """从akshare新浪获取数据"""
        try:
            import akshare as ak
            self._disable_proxy()
            
            # 新浪实时行情
            df = ak.stock_zh_a_spot()
            if not df.empty:
                stock_info = df[df['代码'] == stock_code]
                if not stock_info.empty:
                    return self._parse_sina_data(stock_info.iloc[0])
            return None
        except Exception as e:
            print(f"⚠️ 新浪数据源失败: {e}")
            self._log_request_failure(stock_code, "akshare_sina", e, stage="获取数据")
            return None
    
    def _get_from_akshare_qq(self, stock_code):
        """从akshare腾讯获取数据"""
        try:
            import akshare as ak
            self._disable_proxy()
            
            # 腾讯实时行情
            symbol = f"sh{stock_code}" if stock_code.startswith('6') else f"sz{stock_code}"
            df = ak.stock_zh_a_spot_qq(symbol=symbol)
            if not df.empty:
                return self._parse_qq_data(df.iloc[0])
            return None
        except Exception as e:
            print(f"⚠️ 腾讯数据源失败: {e}")
            self._log_request_failure(stock_code, "akshare_qq", e, stage="获取数据")
            return None
    
    def _get_from_direct_request(self, stock_code):
        """直接HTTP请求获取数据"""
        try:
            import requests
            import json
            
            # 东方财富API
            url = f"http://push2.eastmoney.com/api/qt/stock/get"
            params = {
                'secid': f"1.{stock_code}" if stock_code.startswith('6') else f"0.{stock_code}",
                'fields': 'f43,f44,f45,f46,f47,f48,f49,f50,f51,f52,f57,f58,f116,f117,f162'
            }
            
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Referer': 'http://quote.eastmoney.com/'
            }
            
            response = requests.get(url, params=params, headers=headers, timeout=10)
            if response.status_code == 200:
                data = response.json()
                if data.get('data'):
                    return self._parse_direct_data(data['data'])
            return None
        except Exception as e:
            print(f"⚠️ 直接请求失败: {e}")
            self._log_request_failure(stock_code, "direct_request", e, stage="获取数据")
            return None
    
    def _parse_em_data(self, data):
        """解析东方财富数据"""
        return {
            'code': data.get('代码', ''),
            'name': data.get('名称', ''),
            'current_price': float(data.get('最新价', 0)),
            'close': float(data.get('最新价', 0)),
            'market_cap': float(data.get('总市值', 0)) / 100000000,
            'pe_ratio': float(data.get('市盈率-动态', 0)),
            'pb_ratio': float(data.get('市净率', 0)),
            'turnover_rate': float(data.get('换手率', 0)),
            'volume': float(data.get('成交量', 0))
        }
    
    def _parse_sina_data(self, data):
        """解析新浪数据"""
        return {
            'code': data.get('代码', ''),
            'name': data.get('名称', ''),
            'current_price': float(data.get('最新价', 0)),
            'close': float(data.get('最新价', 0)),
            'market_cap': float(data.get('总市值', 0)) / 100000000,
            'pe_ratio': float(data.get('市盈率', 0)),
            'pb_ratio': float(data.get('市净率', 0)),
            'turnover_rate': float(data.get('换手率', 0)),
            'volume': float(data.get('成交量', 0))
        }
    
    def _parse_qq_data(self, data):
        """解析腾讯数据"""
        return {
            'code': data.get('代码', ''),
            'name': data.get('名称', ''),
            'current_price': float(data.get('现价', 0)),
            'close': float(data.get('现价', 0)),
            'market_cap': float(data.get('总市值', 0)) / 100000000,
            'pe_ratio': float(data.get('市盈率', 0)),
            'pb_ratio': float(data.get('市净率', 0)),
            'turnover_rate': float(data.get('换手率', 0)),
            'volume': float(data.get('成交量', 0))
        }
    
    def _parse_direct_data(self, data):
        """解析直接请求数据"""
        return {
            'code': data.get('f57', ''),
            'name': data.get('f58', ''),
            'current_price': float(data.get('f43', 0)) / 100,
            'close': float(data.get('f43', 0)) / 100,
            'market_cap': float(data.get('f116', 0)) / 100000000,
            'pe_ratio': float(data.get('f162', 0)) / 100,
            'pb_ratio': float(data.get('f46', 0)) / 100,
            'turnover_rate': float(data.get('f48', 0)) / 100,
            'volume': float(data.get('f47', 0))
        }
    
    def _log_request_failure(self, stock_code, source, error, stage="请求", extra=None):
        """记录请求失败日志和类型"""
        import traceback
        if not hasattr(self, 'failure_types'):
            self.failure_types = {}
        err_type = type(error).__name__
        err_msg = str(error)
        key = f"{source}:{err_type}"
        self.failure_types[key] = self.failure_types.get(key, 0) + 1
        print(f"❌ [{stage}] {source} 获取 {stock_code} 失败: {err_type} - {err_msg}")
        if extra:
            print(f"  额外信息: {extra}")
        print(f"  Traceback: {traceback.format_exc(limit=1)}")

    def print_failure_stats(self):
        """打印失败类型统计"""
        if hasattr(self, 'failure_types') and self.failure_types:
            print("\n📉 失败类型统计:")
            for k, v in self.failure_types.items():
                print(f"  {k}: {v} 次")
        else:
            print("\n📉 暂无失败类型统计")
    
    def _enrich_stock_list_data(self, df):
        """丰富股票列表数据，批量获取价格和市值"""
        try:
            print(f"🔄 正在丰富 {len(df)} 只股票的数据...")
            
            # 分批处理，避免请求过多
            batch_size = 50
            total_batches = (len(df) + batch_size - 1) // batch_size
            
            price_data = []
            market_cap_data = []
            
            for i in range(0, len(df), batch_size):
                batch = df.iloc[i:i+batch_size]
                batch_num = i // batch_size + 1
                
                print(f"   处理批次 {batch_num}/{total_batches} ({len(batch)} 只股票)")
                
                for _, row in batch.iterrows():
                    code = row['code']
                    try:
                        # 获取实时价格
                        price_info = self._get_real_time_price(code)
                        if price_info:
                            price_data.append(price_info['price'])
                            market_cap_data.append(price_info.get('market_cap', 0))
                        else:
                            price_data.append(0.0)
                            market_cap_data.append(0.0)
                            
                    except Exception as e:
                        print(f"      获取 {code} 数据失败: {e}")
                        price_data.append(0.0)
                        market_cap_data.append(0.0)
                        
                    # 优化控制请求频率 - 更短延迟
                    time.sleep(0.02)  # 从0.05秒减少到0.02秒
                
                # 批次间暂停 - 更短延迟
                if batch_num < total_batches:
                    time.sleep(0.1)  # 从0.3秒减少到0.1秒
            
            # 更新DataFrame
            df['price'] = price_data
            df['market_cap'] = market_cap_data
            
            # 统计有效数据
            valid_prices = sum(1 for p in price_data if p > 0)
            print(f"✅ 成功获取 {valid_prices}/{len(df)} 只股票的实时数据")
            
        except Exception as e:
            print(f"❌ 丰富股票数据失败: {e}")
            # 使用默认值
            df['price'] = 0.0
            df['market_cap'] = 0.0
    
    def _get_real_time_price(self, code):
        """获取股票实时价格"""
        try:
            # 首先尝试从缓存获取
            cache_key = f"price_{code}"
            cached_price = self._get_from_cache(cache_key)
            if cached_price is not None:
                return cached_price
            
            # 使用akshare获取实时价格
            self._wait_for_request()
            
            # 获取实时行情
            rt_data = ak.stock_zh_a_spot_em()
            if not rt_data.empty:
                stock_data = rt_data[rt_data['代码'] == code]
                if not stock_data.empty:
                    price = float(stock_data['最新价'].iloc[0])
                    market_cap = float(stock_data.get('总市值', 0).iloc[0]) if '总市值' in stock_data.columns else 0
                    
                    price_info = {
                        'price': price,
                        'market_cap': market_cap
                    }
                    
                    # 缓存1小时
                    self._save_to_cache(cache_key, price_info, expire_hours=1)
                    return price_info
            
            return None
            
        except Exception as e:
            # 尝试备用方法
            try:
                # 使用个股数据获取
                data = ak.stock_zh_a_hist(symbol=code, period="daily", start_date="20241201", end_date="20241231", adjust="qfq")
                if not data.empty:
                    price = float(data['收盘'].iloc[-1])
                    price_info = {
                        'price': price,
                        'market_cap': 0
                    }
                    # 缓存1小时
                    self._save_to_cache(cache_key, price_info, expire_hours=1)
                    return price_info
            except:
                pass
            
            return None
    
    def _get_fallback_stock_list(self):
        """获取备用股票列表"""
        try:
            print("🔄 使用备用股票数据源...")
            
            # 扩展的备用股票列表，包含各行业优质股票
            fallback_stocks = [
                # 银行板块
                {'code': '000001', 'name': '平安银行', 'price': 12.85, 'market_cap': 248.7},
                {'code': '600036', 'name': '招商银行', 'price': 18.65, 'market_cap': 965.4},
                {'code': '002142', 'name': '宁波银行', 'price': 19.45, 'market_cap': 187.3},
                {'code': '601166', 'name': '兴业银行', 'price': 12.45, 'market_cap': 298.6},
                {'code': '000776', 'name': '广发证券', 'price': 17.69, 'market_cap': 107.2},
                
                # 白酒板块
                {'code': '600519', 'name': '贵州茅台', 'price': 1427.0, 'market_cap': 1793.2},
                {'code': '000858', 'name': '五粮液', 'price': 89.5, 'market_cap': 346.8},
                {'code': '000596', 'name': '古井贡酒', 'price': 56.78, 'market_cap': 89.4},
                {'code': '002304', 'name': '洋河股份', 'price': 78.45, 'market_cap': 117.6},
                {'code': '000725', 'name': '京东方A', 'price': 4.06, 'market_cap': 141.2},
                
                # 科技板块
                {'code': '002475', 'name': '立讯精密', 'price': 15.23, 'market_cap': 107.8},
                {'code': '000063', 'name': '中兴通讯', 'price': 7.82, 'market_cap': 377.6},
                {'code': '002415', 'name': '海康威视', 'price': 14.23, 'market_cap': 133.9},
                {'code': '002230', 'name': '科大讯飞', 'price': 16.78, 'market_cap': 366.1},
                {'code': '300059', 'name': '东方财富', 'price': 10.56, 'market_cap': 168.4},
                
                # 新能源板块
                {'code': '002594', 'name': '比亚迪', 'price': 175.0, 'market_cap': 5089.7},
                {'code': '300750', 'name': '宁德时代', 'price': 189.12, 'market_cap': 830.2},
                {'code': '688599', 'name': '天合光能', 'price': 45.67, 'market_cap': 89.3},
                {'code': '002459', 'name': '晶澳科技', 'price': 23.45, 'market_cap': 67.8},
                {'code': '300274', 'name': '阳光电源', 'price': 78.90, 'market_cap': 112.4},
                
                # 医药板块
                {'code': '600196', 'name': '复星医药', 'price': 25.6, 'market_cap': 99.7},
                {'code': '300015', 'name': '爱尔眼科', 'price': 8.65, 'market_cap': 138.2},
                {'code': '000661', 'name': '长春高新', 'price': 123.45, 'market_cap': 156.7},
                {'code': '300760', 'name': '迈瑞医疗', 'price': 234.56, 'market_cap': 289.4},
                {'code': '002821', 'name': '凯莱英', 'price': 67.89, 'market_cap': 78.9},
                
                # 消费板块
                {'code': '000002', 'name': '万科A', 'price': 6.89, 'market_cap': 758.2},
                {'code': '600900', 'name': '长江电力', 'price': 13.45, 'market_cap': 301.8},
                {'code': '601318', 'name': '中国平安', 'price': 17.93, 'market_cap': 327.5},
                {'code': '000858', 'name': '五粮液', 'price': 89.5, 'market_cap': 346.8},
                {'code': '002714', 'name': '牧原股份', 'price': 45.67, 'market_cap': 234.5},
                
                # 工业板块
                {'code': '600031', 'name': '三一重工', 'price': 12.34, 'market_cap': 98.7},
                {'code': '000338', 'name': '潍柴动力', 'price': 14.56, 'market_cap': 123.4},
                {'code': '002271', 'name': '东方雨虹', 'price': 23.45, 'market_cap': 67.8},
                {'code': '600585', 'name': '海螞水泥', 'price': 16.78, 'market_cap': 89.2},
                {'code': '000651', 'name': '格力电器', 'price': 34.56, 'market_cap': 201.3},
                
                # 材料板块
                {'code': '002460', 'name': '赣锋锂业', 'price': 56.78, 'market_cap': 89.4},
                {'code': '002466', 'name': '天齐锂业', 'price': 67.89, 'market_cap': 98.5},
                {'code': '000967', 'name': '盈峰环境', 'price': 12.34, 'market_cap': 34.5},
                {'code': '002709', 'name': '天赐材料', 'price': 23.45, 'market_cap': 56.7},
                {'code': '300014', 'name': '亿纬锂能', 'price': 45.67, 'market_cap': 78.9},
                
                # 通信板块
                {'code': '600050', 'name': '中国联通', 'price': 4.56, 'market_cap': 145.6},
                {'code': '000876', 'name': '新希望', 'price': 12.34, 'market_cap': 56.7},
                {'code': '002049', 'name': '紫光国微', 'price': 78.90, 'market_cap': 234.5},
                {'code': '300496', 'name': '中科创达', 'price': 67.89, 'market_cap': 89.4},
                {'code': '688012', 'name': '中微公司', 'price': 123.45, 'market_cap': 156.7},
                
                # 金融板块
                {'code': '601688', 'name': '华泰证券', 'price': 12.34, 'market_cap': 234.5},
                {'code': '600030', 'name': '中信证券', 'price': 15.67, 'market_cap': 456.7},
                {'code': '000783', 'name': '长江证券', 'price': 6.78, 'market_cap': 78.9},
                {'code': '600999', 'name': '招商证券', 'price': 9.87, 'market_cap': 123.4},
                {'code': '002797', 'name': '第一创业', 'price': 4.56, 'market_cap': 45.6},
                
                # 更多优质股票
                {'code': '300142', 'name': '沃森生物', 'price': 23.45, 'market_cap': 67.8},
                {'code': '688981', 'name': '中芯国际', 'price': 45.67, 'market_cap': 234.5},
                {'code': '002410', 'name': '广联达', 'price': 34.56, 'market_cap': 89.4},
                {'code': '300450', 'name': '先导智能', 'price': 56.78, 'market_cap': 123.4},
                {'code': '300750', 'name': '宁德时代', 'price': 189.12, 'market_cap': 830.2}
            ]
            
            fallback_df = pd.DataFrame(fallback_stocks)
            print(f"✅ 获取备用股票列表: {len(fallback_df)} 只股票")
            
            # 尝试更新部分实时数据
            self._update_fallback_prices(fallback_df)
            
            return fallback_df
            
        except Exception as e:
            print(f"❌ 获取备用股票列表失败: {e}")
            return pd.DataFrame()
    
    def _update_fallback_prices(self, df):
        """更新备用数据的价格"""
        try:
            print("🔄 尝试更新备用数据的实时价格...")
            updated_count = 0
            
            for i, row in df.iterrows():
                try:
                    code = row['code']
                    price_info = self._get_real_time_price(code)
                    if price_info and price_info['price'] > 0:
                        df.at[i, 'price'] = price_info['price']
                        if price_info['market_cap'] > 0:
                            df.at[i, 'market_cap'] = price_info['market_cap']
                        updated_count += 1
                        
                    # 优化控制请求频率 - 最小延迟
                    time.sleep(0.02)  # 从0.1秒减少到0.02秒
                    
                except Exception as e:
                    print(f"   更新 {code} 价格失败: {e}")
                    continue
            
            print(f"✅ 成功更新 {updated_count} 只股票的实时价格")
            return df
            
        except Exception as e:
            print(f"⚠️ 更新备用价格失败: {e}")
            
    def get_multiple_stock_data(self, stock_codes, days=120, max_workers=10):
        """批量获取多只股票数据 - 优化版本"""
        try:
            print(f"� 开始高速批量获取 {len(stock_codes)} 只股票数据...")
            
            results = {}
            failed_stocks = []
            
            # 增加批处理大小，提高效率
            batch_size = 30  # 从20增加到30
            total_batches = (len(stock_codes) + batch_size - 1) // batch_size
            
            for i in range(0, len(stock_codes), batch_size):
                batch = stock_codes[i:i+batch_size]
                batch_num = i // batch_size + 1
                
                print(f"   ⚡ 处理批次 {batch_num}/{total_batches} ({len(batch)} 只股票)")
                
                # 单线程处理每个批次，确保稳定性
                batch_results = {}
                for stock_code in batch:
                    try:
                        data = self.get_stock_data(stock_code, days)
                        if not data.empty:
                            batch_results[stock_code] = data
                        else:
                            failed_stocks.append(stock_code)
                            
                    except Exception as e:
                        print(f"   获取 {stock_code} 失败: {e}")
                        failed_stocks.append(stock_code)
                        
                    # 优化控制请求频率 - 减少延迟
                    time.sleep(0.2)
                
                results.update(batch_results)
                
                # 批次间暂停 - 最小延迟
                if batch_num < total_batches:
                    print(f"   批次 {batch_num} 完成，获取 {len(batch_results)} 只股票数据")
                    time.sleep(0.1)  # 从0.5秒减少到0.1秒
            
            success_count = len(results)
            print(f"✅ 批量获取完成: 成功 {success_count}/{len(stock_codes)} 只股票")
            
            if failed_stocks:
                print(f"⚠️ 获取失败的股票: {failed_stocks[:10]}{'...' if len(failed_stocks) > 10 else ''}")
            
            return results
            
        except Exception as e:
            print(f"❌ 批量获取股票数据失败: {e}")
            return {}
    
    def get_stock_data_for_analysis(self, stock_list, days=120):
        """为分析获取股票数据"""
        try:
            print(f"🔄 为分析获取 {len(stock_list)} 只股票数据...")
            
            # 如果stock_list是DataFrame，提取代码列
            if isinstance(stock_list, pd.DataFrame):
                if 'code' in stock_list.columns:
                    stock_codes = stock_list['code'].tolist()
                else:
                    stock_codes = stock_list.index.tolist()
            else:
                stock_codes = stock_list
            
            # 批量获取数据
            stock_data = self.get_multiple_stock_data(stock_codes, days)
            
            # 统计结果
            success_count = len(stock_data)
            total_count = len(stock_codes)
            
            print(f"📊 数据获取统计:")
            print(f"   • 成功获取: {success_count} 只股票")
            print(f"   • 获取失败: {total_count - success_count} 只股票")
            print(f"   • 成功率: {success_count/total_count*100:.1f}%")
            
            return stock_data
            
        except Exception as e:
            print(f"❌ 获取分析数据失败: {e}")
            return {}
    
    def update_stock_prices(self, stock_list):
        """更新股票价格信息"""
        try:
            print(f"🔄 更新 {len(stock_list)} 只股票价格...")
            
            updated_count = 0
            
            for i, row in stock_list.iterrows():
                try:
                    code = row['code']
                    price_info = self._get_real_time_price(code)
                    
                    if price_info and price_info['price'] > 0:
                        stock_list.at[i, 'price'] = price_info['price']
                        if price_info['market_cap'] > 0:
                            stock_list.at[i, 'market_cap'] = price_info['market_cap']
                        updated_count += 1
                        
                    # 控制请求频率 - 最小延迟
                    time.sleep(0.01)  # 从0.2秒减少到0.01秒
                    
                except Exception as e:
                    print(f"   更新 {code} 价格失败: {e}")
                    continue
            
            print(f"✅ 成功更新 {updated_count} 只股票价格")
            return stock_list
            
        except Exception as e:
            print(f"❌ 更新股票价格失败: {e}")
            return stock_list
    
    def set_speed_mode(self, mode='fast'):
        """设置速率控制模式"""
        self.speed_mode = mode
        
        if mode == 'fast':
            self.request_interval = 0.1  # 进一步减少到0.1秒
            self.min_interval = 0.05  # 最小间隔0.05秒
            self.max_interval = 0.5  # 最大间隔0.5秒
            print("🚀 已切换到超快速模式")
        elif mode == 'normal':
            self.request_interval = 0.5
            self.min_interval = 0.2
            self.max_interval = 1.0
            print("⚡ 已切换到正常模式")
        elif mode == 'slow':
            self.request_interval = 1.0
            self.min_interval = 0.5
            self.max_interval = 2.0
            print("🐌 已切换到慢速模式")
